package cc.lishixi.tdd.concurrent;

/**
 * @author LiShixi 2021/1/3 8:09 下午
 * @email loyy77@163.com
 */

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ResourceLock;

import java.util.Properties;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;
import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ;
import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE;
import static org.junit.jupiter.api.parallel.Resources.SYSTEM_PROPERTIES;

/**
 * 同步
 * 除了通过@Execution注解设置测试并发执行模式之外，JUnit Jupiter还提供了另外一个基于注解的声明式同步机制。@ResourceLock注解允许你声明一个测试类或测试方法使用一个共享的、需要同步访问的资源来保证测试的可靠执行。这个共享资源通过一个唯一的名称来标识。这个名称可以是自定义的，也可以是系统预定义的Resources常量之一：SYSTEM_PROPERTIES, SYSTEM_OUT, SYSTEM_ERR, LOCALE 或 TIME_ZONE。
 * 下面示例中的测试类如果没有使用@ResourceLock注解而在并发模式下执行，测试是脆弱的，测试有时会通过，有时会因为对同一个JVM系统属性先写后读的内在竞态条件而失败。当使用@ResourceLock注解声明共享资源之后，JUnit Jupiter使用这个信息保证相互冲突的测试不会并行执行。
 * 在@ResourceLock注解中还可以指定资源的访问模式。两个需要READ访问同一个共享资源的测试可以并发执行，但任何一个测试需要对共享资源进行READ_WRITE时，两个测试只能顺序执行。
 * @author LiShixi 2021/1/3 7:33 下午
 * @email loyy77@163.com
 */
@Execution(CONCURRENT)
class SharedResourcesDemo {

    private Properties backup;

    @BeforeEach
    void backup() {
        backup = new Properties();
        backup.putAll(System.getProperties());
    }

    @AfterEach
    void restore() {
        System.setProperties(backup);
    }

    @Test
    @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ)
    void customPropertyIsNotSetByDefault() {
        assertThat(System.getProperty("my.prop")).isNull();
    }

    @Test
    @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
    void canSetCustomPropertyToApple() {
        System.setProperty("my.prop", "apple");
        assertThat(System.getProperty("my.prop")).isEqualTo("apple");
    }

    @Test
    @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
    void canSetCustomPropertyToBanana() {
        System.setProperty("my.prop", "banana");
        assertThat(System.getProperty("my.prop")).isEqualTo("banana");
    }
}
